use std::{fs, io};
use std::fs::{File, Permissions};
use std::io::{Read, Seek};
use std::os::unix::fs::PermissionsExt;
use std::path::Path;

use indicatif::{ProgressBar, ProgressStyle};
use zip::ZipArchive;

use crate::runtime::runtime;
use crate::runtime::runtime::error::InternalError;
use crate::util::util::reader::proxy::Proxy;

/// 解压zip文件
pub fn decompress<R: Read + Seek, P: AsRef<Path>>(rp: P, r: R, s: u64, t: P) -> runtime::result::InternalErrorResult<()> {
    println!("解压文件: {} -> {}", rp.as_ref().to_str().unwrap(), t.as_ref().to_str().unwrap());

    // 进度条
    let pb = ProgressBar::new(s);
    pb.set_style(ProgressStyle::with_template("{spinner:.green} 解压文件 [{elapsed_precise}] [{wide_bar:.cyan/blue}] {bytes}/{total_bytes}").unwrap().progress_chars("=>-"));

    let proxy = Proxy::new(r, |us| pb.inc(us as u64));
    let mut zip = ZipArchive::new(proxy).map_err(|e| InternalError::new(format!("Could not decompress zip: {}", e)))?;

    for i in 0..zip.len() {
        let mut src = zip.by_index(i).map_err(|e| InternalError::new(format!("Could not decompress zip: {}", e)))?;

        let path = t.as_ref().join(src.name());
        if src.is_file() {
            fs::create_dir_all(path.parent().unwrap()).map_err(|e| InternalError::new(format!("Could not decompress zip: {}", e)))?;
            let mut dst = File::create(path.clone()).map_err(|e| InternalError::new(format!("Could not decompress zip: {}", e)))?;
            io::copy(&mut src, &mut dst).map_err(|e| InternalError::new(format!("Could not decompress zip: {}", e)))?;

            if src.unix_mode().is_some() {
                fs::set_permissions(path, Permissions::from_mode(src.unix_mode().unwrap())).map_err(|e| InternalError::new(format!("Could not decompress zip: {}", e)))?;
            }
        }
    }

    Ok(())
}

#[cfg(test)]
mod tests {
    use reqwest::Url;
    use tempfile;

    use crate::util::util::network;

    #[test]
    fn test_decompress_zip() {
        let url = Url::parse("https://gitee.com/BluePointLilac/ContextMenuManager/releases/download/3.3.3.1/ContextMenuManager.zip");
        assert_eq!(true, url.is_ok());

        let result = network::downloader::download(url.unwrap());
        assert_eq!(true, result.is_ok(), "下载文件失败: {}", result.unwrap_err());

        let (f, s) = result.unwrap();

        let temp_dir = tempfile::tempdir();
        assert_eq!(true, temp_dir.is_ok(), "创建临时文件夹失败: {}", temp_dir.unwrap_err());

        let result = super::decompress(f.path(), &f, s, temp_dir.unwrap().path());
        assert_eq!(true, result.is_ok(), "解压文件失败: {}", result.unwrap_err());
    }
}